

//@author A0101856J
//The History class allows users to perform operations such as undo and redo.
//Undo will restore the list of tasks to the original state before the most recent operation
// and redo will restore the list of tasks to the original state before the most recent undo.
//Both undo and redo are written to work for undo-able operations such as
// add, delete, edit, and mark. Undo functions will not work for operations such
// as display and summary.
//In addition, the redo operation will be invalid if a user performs an undo-able function
//after the most recent undo.

#include "History.h"

std::stack<CommandType> History::undoCommandStack;
std::stack<CommandType> History::redoCommandStack;

std::stack<Task*> History::undoTaskStack;
std::stack<Task*> History::redoTaskStack; 


History::~History()
{
	Task* taskToClear;

	while (!undoTaskStack.empty())
	{
		taskToClear = undoTaskStack.top();
		undoTaskStack.pop();
		delete taskToClear;
	}

	while (!redoTaskStack.empty())
	{
		taskToClear = redoTaskStack.top();
		redoTaskStack.pop();
		delete taskToClear;
	}
}

//Whenever an undo-able action (add, delete, and mark) is performed
//the action is stored in the undoCommandStack.
void History::storeAction(CommandType command, Task* task)
{
	assert(command != UPDATE);
	undoCommandStack.push(command);
	undoTaskStack.push(task);
	clearRedoStacks();
}

//stores edit operations in the undoCommandStack
void History::storeAction(CommandType command, Task* outdatedTask, Task* updatedTask)
{
	assert(command == UPDATE);
	undoCommandStack.push(command);
	undoTaskStack.push(outdatedTask);
	undoTaskStack.push(updatedTask);
	clearRedoStacks();
}

void History::clearRedoStacks()
{
	Task* taskToClear;

	while (!redoCommandStack.empty())
	{
		if (redoCommandStack.top() == ADD) {
			redoCommandStack.pop();

			taskToClear = redoTaskStack.top();
			redoTaskStack.pop();
			delete taskToClear;
		}
		else if (redoCommandStack.top() == UPDATE) {
			redoCommandStack.pop();
			redoTaskStack.pop();
			redoTaskStack.pop();
		}
		else {
			redoCommandStack.pop();
			redoTaskStack.pop();
		}
	}
}

CommandType History::getPreviousCommand()
{
	return undoCommandStack.top();
}

void History::getPreviousTaskInfo(Task* &previousTask)
{
	previousTask = undoTaskStack.top();
}

void History::deletePreviousCommand()
{
	undoCommandStack.pop();
}

void History::deletePreviousTask()
{
	undoTaskStack.pop();
}


//undo the most recent undo-able command.
ResultType History::undoPreviousCommand()
{
	CommandType previousUserCommand;

	Task* previousTask = NULL;
	//task before an update
	Task* outdatedTask = NULL;

	if (undoCommandStack.empty()) {
		return ERROR_PROCESSING_COMMAND;
	}

	previousUserCommand = getPreviousCommand();
	deletePreviousCommand();
	getPreviousTaskInfo(previousTask);
	deletePreviousTask();

	redoCommandStack.push(previousUserCommand);

	switch (previousUserCommand)
	{
	//most recent command adds a task.
	//undo deletes the task added.
	case ADD:
		{
			TaskList::deleteTask(previousTask);
			redoTaskStack.push(previousTask);
			break;
		}
	//most recent command deletes a task
	//undo adds the task back.
	case DELETE:
		{
			TaskList::addTask(previousTask);
			redoTaskStack.push(previousTask);
			break;
		}

	//During an update, the outdated task is first deleted, and the updated task is added in.
	//To Undo, the updated task is deleted, and the outdated task is added back in.
	case UPDATE:
		{
			redoTaskStack.push(previousTask);
			getPreviousTaskInfo(outdatedTask);
			redoTaskStack.push(outdatedTask);
			deletePreviousTask();


			//deleting the updated task
			TaskList::deleteTask(previousTask);
			//adding back the task before the update
			TaskList::addTask(outdatedTask);
			break;
		}
	
	//Most recent operation marks a task as completed.
	//Undo removes the mark for the task.
	case MARK:
		{
			redoTaskStack.push(previousTask);
			previousTask->markUndone();
			break;
			
		}
	case UNMARK:
		{
			redoTaskStack.push(previousTask);
			previousTask->markDone();
			break;
		}

	
	}

	return SUCCESS;

}

CommandType History::getCommandToRedo()
{
	return redoCommandStack.top();
}


void History::getTaskToRedo(Task* &taskToRedo)
{
	taskToRedo = redoTaskStack.top();
}

void History::deletePreviousRedo()
{
	redoCommandStack.pop();
}

void History::deletePreviousRedoTask()
{
	redoTaskStack.pop();
}


ResultType History::redoPreviousCommand()
{
	CommandType commandToRedo;

	Task* taskToRedo = NULL;
	//task after an update
	Task* newTasktoRedo = NULL;

	if (redoCommandStack.empty())
	{
		return ERROR_PROCESSING_COMMAND;
	}

	commandToRedo = getCommandToRedo();
	deletePreviousRedo();

	getTaskToRedo(taskToRedo);
	deletePreviousRedoTask();

	undoCommandStack.push(commandToRedo);

	switch (commandToRedo)
	{
	case ADD:
		{
			TaskList::addTask(taskToRedo);
			undoTaskStack.push(taskToRedo);
			break;
			
		}

	case DELETE:
		{
			TaskList::deleteTask(taskToRedo);
			undoTaskStack.push(taskToRedo);
			break;
		}

	//when a user undo an update, the updated task is deleted
	//and the outdated task is added back in.
	//To redo, the outdated task is deleted.
	//and the updated task is added back in.
	case UPDATE:
		{

			undoTaskStack.push(taskToRedo);

			getTaskToRedo(newTasktoRedo);
			deletePreviousRedoTask();
			
			undoTaskStack.push(newTasktoRedo);
			TaskList::deleteTask(taskToRedo);
			TaskList::addTask(newTasktoRedo);

			break;

		}

	case MARK:
		{
			undoTaskStack.push(taskToRedo);
			taskToRedo->markDone();
			break;
		}


	case UNMARK:
		{
			undoTaskStack.push(taskToRedo);
			taskToRedo->markUndone();
			break;
		}
	}
	return SUCCESS;
}

